Skip to content

Add SAP Business Partner example for the ontology layer - #310

Open
marioapiphani wants to merge 1 commit into
apache:mainfrom
marioapiphani:example-sap-bp-ontology
Open

Add SAP Business Partner example for the ontology layer#310
marioapiphani wants to merge 1 commit into
apache:mainfrom
marioapiphani:example-sap-bp-ontology

Conversation

@marioapiphani

Copy link
Copy Markdown

examples/ has one ontology-layer example today (flights.yaml). This adds a second, drawn from an enterprise ERP domain, that exercises ontology constructs the existing example does not reach:

  • entity subtypingCustomer declares extends: [ BusinessPartner ]; flights.yaml has no EntityType subtyping
  • a derived populationCustomer uses concept-level derived_by to define its population as exactly those business partners carrying a customer number, rather than asserting it. flights.yaml uses derived_by only on relationships, for derived attributes.
  • four-part compound identityCustomerSalesArea is identified by customer + sales organization + distribution channel + division. The existing example tops out at two parts.

It also covers ground shared with flights.yaml — value types with requires, associations with and without functional multiplicity, verbalizations in both reading directions, and ontology_mappings down to a logical model — so it stands alone as a reading example.

Source-system provenance (SAP Data Dictionary domains and check tables) is recorded in the descriptions, showing one way to keep technical origin alongside a business-level model.

The README's examples/ entry is updated to name both ontology-layer models.

The file validates against ontology/ontology.json as shipped, with core-spec/osi-schema.json registered to resolve the remote $ref from the checkout:

import json, re, yaml
from jsonschema import Draft202012Validator
from referencing import Registry, Resource
onto = open("ontology/ontology.json").read()
core = Resource.from_contents(json.load(open("core-spec/osi-schema.json")))
reg = Registry().with_resources([(u, core) for u in set(re.findall(r'"\$ref": "(https://[^"#]+)', onto))])
v = Draft202012Validator(json.loads(onto), registry=reg)
print(sorted(v.iter_errors(yaml.safe_load(open("examples/sap_business_partner_ontology.yaml"))), key=lambda e: list(e.path)) or "VALID")

Prints VALID. No code changes.

🤖 Generated with Claude Code

An ontology-layer model of a Business Partner domain, exercising entity
subtyping with a derived population, four-part compound identity, value
types with requires constraints, associations with and without functional
multiplicity, verbalizations, and ontology_mappings to a logical model.

Validates against ontology/ontology.json as shipped.
@jbonofre
jbonofre self-requested a review August 9, 2026 13:26
@jochenchrist

Copy link
Copy Markdown
Contributor

Could you provide the original data model?

@jochenchrist

Copy link
Copy Markdown
Contributor

Also curious, if there are some properties missing in Ossie?

a closed-world assumption.
extends: [ BusinessPartner ]
derived_by:
- "EXISTS ( BusinessPartner.customer_nr )"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

derived_by requires EXISTS ( BusinessPartner.customer_nr) , but customer_nr has no link_mapping anywhere in the BusinessPartner nor CUSTOMER_SALES_AREA has a field that could feed it.

Running this file's own mapping yields zero Customer objects, so the derived-population feature this example is meant to demonstrate can't actually be observed.

derived_by:
- "EXISTS ( BusinessPartner.customer_nr )"
relationships:
- name: has_sales_area

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no concept: Customer entry anywhere in concept_mappings, only BusinessPartner and CustomerSalesArea are mapped.

has_sales_area gets zero link_mappings and zero derived_by, so this relationship (meant to illustrate "association without functional multiplicity") can never by populated.

relationships:
- name: customer
roles:
- concept: Customer

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These look like the same real-world relationship modeled as two independent declarations instead of one dual-verbalized relationship, the pattern ontology.md's Person.parent_of uses.

That means keeping them in sync by hand, and as noted above, the Customer.has_sales_area side isn't even mapped.

multiplicity: ManyToOne
verbalizes:
- "{CustomerSalesArea} covers division {Division}"
- name: delivery_blocked

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DELIVERY_BLOCK exists in the CUSTOMER_SALES_AREA dataset specifically for this, but CustomerSalesArea's concept_mapping stops after division, there's no delivery_blocked link_mapping. Compare flights.yaml's canceled flag, mapped via FLIGHT.id WHERE ( FLIGHT.cancelled == TRUE ), for the pattern this is missing.

- relationship: nr
expression: "BUSINESS_PARTNER.BP_NUMBER"
children:
- relationship: BusinessPartner.category

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These LinkMapping.relationship values are concept-qualified, but ontology.md's worked example and every occurence in flights.yaml use bare relationship names for LinkMapping.relationship (concept-qualification is for ReferentMapping.relationship instead).

If the mapping engine resolves by exact local name, category/full_name/country would never resolve and stay unpopulated.

population from a country dataset rather than enumerating values here.
extends: [ String ]
requires:
- "LENGTH(CountryCode) = 2"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the only bare = in the file/corpus, every sibling constraint uses <= and ontology.md/flights.yaml use == exclusivity for equality. Worth double-checking the ontology-layer expression dialect accepts bare =. Separately, SAP's LAND1 field is commonly CHAR(3), so an exact length-2 constraint would reject legitimate 3-character country codes.

- referent_mappings:
- relationship: customer
referent_mappings:
- relationship: BusinessPartner.nr

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Customer declares no identify_by of its own, and neither the spec nor the schema says whether identify_by is inherited via extends ("inherit" doesn't appear anywhere in ontology.md/spec.md/the JSON schemas).

Worth flagging that against the actual converter, identify_by is built only from a concept's own YAML list, so Customer.identify_by resolved {} here.

On top of that, lookup_concept_relationship always prefixes f"{concept.name}.{name}", so an already-qualified value like BusinessPartner.nr would get double-prefixed and fail to resolve. This isn't specific to this file (it'd hit ontology.md's own CustOrder.nr example too), but this PR looks like the first example to actually exercise this path.

- concept: CountryCode
type: ValueType
description: >
Country key. Technical origin: DDIC domain LAND1, check table T005.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This says population comes "from a country dataset" preserving the T005 check-table relationship, but no such dataset exists in ontology_mappings (the actual mapping just copies BUSINESS_PARTNER.COUNTRY straight through.

It might be worth correcting the description or adding the dataset it describes.


# ---- Value types carrying DDIC domain semantics -------------------------

- concept: BusinessPartnerNumber

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The six sibling ValueType.requires blocks mix block-list and inline-flow YAML style for structurally identical constraints. Given this file is meant as a template for future SAP fields, a consistent style would help (and might have caught the = 2 issue 😄 ).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants